﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.IO;
using System.Security.AccessControl;
using System.ServiceModel;
using System.Text;
using System.Xml.Linq;
using System.Xml.XPath;
using Microsoft.ServiceBus;
using Microsoft.Synchronization;
using Microsoft.WindowsAzure.Accelerator.Configuration;
using Microsoft.WindowsAzure.Accelerator.Diagnostics;
using Microsoft.WindowsAzure.Accelerator.Properties;
using Microsoft.WindowsAzure.Accelerator.Synchronization;
using Microsoft.WindowsAzure.StorageClient;
using Console = System.Console;
using Enumerable = System.Linq.Enumerable;
using Environment = System.Environment;


namespace Microsoft.WindowsAzure.Accelerator
{
    /// <summary>
    /// Windows Azure accelerator engine console application. (i|rdm)
    /// </summary>
    public class AccelCon
    {
        private Dictionary<char, string> _switches;
        private List<string> _params;
        private Settings _settings;
        
        /// <summary>
        /// Gets a list of the command line switches/options.
        /// </summary>
        public Dictionary<char, string> Switches { get { return _switches ?? (_switches = new Dictionary<char, string>()); } }
        
        /// <summary>
        /// Gets a list the command line parameters.
        /// </summary>
        public List<string> Params { get { return _params ?? (_params = new List<string>()); } }

        /// <summary>
        /// Gets a list service configuration settings.
        /// </summary>
        public Settings Settings { get { return _settings ?? (_settings = new Settings()); } }

        /// <summary>
        /// Accelerator console application main entry point.
        /// </summary>
        /// <param name="args">Command line arguments.</param>
        public AccelCon(String[] args)
        {
            Boolean validArgs = true;
            
            //i| Grab the switches, options (ignores case); and parameters (retains case).
            foreach (var arg in args)
            {
                //i| Attempt to prevent matching virtual directories /vdir as a switch.
                //i| Its only a switch if its 2 chars long; or if its 4+ with a colon at 3.
                //i| /q, /w, /c:u ...
                if (arg.Length > 1 && (arg[0] == '/' || arg[0] == '-'))
                    if (arg.Length > 4) 
                        if (arg[2] == ':') 
                            Switches.Add(Char.ToLower(arg[1]), arg.Substring(3).ToLower() );
                        else
                            Params.Add(arg);
                    else
                        Switches[Char.ToLower(arg[1])] = String.Empty;
                else
                    Params.Add(arg); //i| Grab the params (non-switches); case-sensitive for blob storage.
            }

            try
            {
                if ( Switches.Count < 1 ) validArgs = false;
                else //i| Switches can be in any order.
                {
                    //i|
                    //i| Upload
                    //i|
                    if ( Switches.ContainsKey('u') )
                    {
                        //i|
                        //i| BlobSync
                        //i|
                        if ( Switches.ContainsKey('s') && Params.Count == 2 )
                        {
                            SynchBlobStorage(SyncDirectionOrder.Upload, Params[1], Params[0]);
                        }
                            //i|
                            //i| CloudDrive
                            //i|
                        else if ( Switches.ContainsKey('v') )
                        {
                            UploadFile(Params[0], Params.Count == 1 ? Settings.DefaultCloudDriveUri : Params[1], BlobType.PageBlob, Switches.ContainsKey('q'));
                        }
                            //i|
                            //i| WebSite
                            //i|
                        else if (Switches.ContainsKey('w'))
                        {
                            BuildSolution(Params.Count > 0 ? Params[0] : Settings.DefaultCloudDriveUri, BlobType.PageBlob, Switches.ContainsKey('q'), true);
                            PublishWeb(Params.Count > 0 ? Params[0] : Settings.DefaultLocalSitePath, Params.Count > 1 ? Params[1] : Settings.DefaultCloudDriveUri, BlobType.PageBlob, Switches.ContainsKey('q'), true);
                        }
                            //i|
                            //i| BlockBlob 
                            //i|
                        else if (Params.Count > 0)
                        {
                            UploadFile(Params[0], Params[1], BlobType.BlockBlob, Switches.ContainsKey('q'));
                        }
                    }
                    else if ( Switches.ContainsKey('w') )
                    {
                        BuildSolution(Params.Count > 0 ? Params[0] : Settings.DefaultCloudDriveUri, BlobType.PageBlob, Switches.ContainsKey('q'), false);
                        //PublishWeb(Params.Count > 0 ? Params[0] : Settings.DefaultLocalSitePath, Params.Count > 1 ? Params[1] : Settings.DefaultCloudDriveUri, BlobType.PageBlob, Switches.ContainsKey('q'), false);
                    }
                        //i|
                        //i| Download
                        //i|
                    else if ( Switches.ContainsKey('d') && Params.Count == 2 )
                    {
                        //i|
                        //i| BlobSync
                        //i|
                        if ( Switches.ContainsKey('s') ) SynchBlobStorage(SyncDirectionOrder.DownloadAndUpload, Params[0], Params[1]);
                            //i|
                            //i| BlockBlob
                            //i|
                        else DownloadFile(Params[0], Params[1], Switches.ContainsKey('q'));
                    }
                        //i|
                        //i| Set Environment
                        //i|
                    else if (Switches.ContainsKey('e')) SetEnvironment(Params.Count > 0 ? Params[0] : String.Empty);
                        //i|
                        //i| Service Console
                        //i|
                    else if ( Switches.ContainsKey('c') ) CreateServiceConsole(Params[0] ?? String.Empty);
                        //i|
                        //i| Trace Console
                        //i|
                    else if ( Switches.ContainsKey('t') ) CreateTraceListener(Params.Count > 0 ? Params[0] : String.Empty);
                    else validArgs = false;
                }
            }
            catch(System.IO.FileLoadException ex)
            {
                Console.Error.WriteLine("\r\n{0}\r\n'{1}'", ex.ToTraceString(), ex.FileName);
            }
            catch(FileNotFoundException ex)
            {
                Console.Error.WriteLine("\r\n{0}\r\n'{1}'", ex.ToTraceString(), ex.FileName);
            }
            catch (Exception ex)
            {
                Console.Error.WriteLine("\r\n{0}", ex.ToTraceString());
            }
            //i| Display help.
            if ( !validArgs )
            {
                Console.WriteLine(Resources.Usage);
            }
        }

        /// <summary>
        /// Parses a Service Configuration settings file and populates each into a Environment Variable by the same name.  
        /// Connections strings settings are parsed into their components. Settings which are duplicated are overwritten 
        /// by last in DOM order wins.
        /// </summary>
        /// <param name="programPath">Optional argument for a system binary or batch file to start with the environment variables set.</param>
        private void SetEnvironment(String programPath)
        {
            Settings.SetConfigEnvironment();            
            if (!String.IsNullOrEmpty(programPath) && File.Exists(programPath = Path.GetFullPath(programPath)))
            {
                ConsoleProcess.Start(programPath, Path.GetFullPath("."), true, String.Empty);                
            }
        }

        /// <summary>
        /// Performs the delegate function on each of a sites physicalPath locations.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="U"></typeparam>
        /// <param name="func">The func.</param>
        /// <returns></returns>
        public U SitePathActions<T,U>(Func<T, U> func) where U : Path
        {
            return null;
        }
        
        /// <summary>
        /// Copies the site.
        /// </summary>
        /// <param name="source">The source.</param>
        /// <param name="target">The target.</param>
        public static void CopySite(String source, String target)
        {
              Path copySite = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"Scripts\copySite.bat");
              String args = String.Format(@"""{0}"" ""{1}""", source, target);
              ConsoleProcess.Start(copySite, System.IO.Path.GetFullPath("."), true, args);
        }

        /// <summary>
        /// Creates the sites directories.
        /// </summary>
        /// <remarks>
        /// BUGBUG:  (b|rdm)
        /// Mirroring the entire physical structure is not correct.  Virtual directories should retain
        /// their unique physical separation characteristics.  This is a bug.  I need to figure out how
        /// Azure does this and mirror their virtual directory process.
        /// </remarks>
        /// <param name="element">The element.</param>
        /// <param name="targetPath">The target path.</param>
        /// <returns></returns>
        public void CreateSiteDirectories(XElement element, String targetPath)
        {
            //i|
            //i|
            //i|
            String name = element.GetAttribute("name");
            if (String.IsNullOrEmpty(name)) throw new ArgumentException(String.Format("Invalid VirtualDirectory name '{0}'.", name));
            Path target = System.IO.Path.Combine(targetPath, name);

            //i| First, check if we have a reset on the physical directory; 
            //i| if so we need to copy from a new location.)
            if (!String.IsNullOrEmpty(element.GetAttribute("physicalDirectory")))
            {
                //i| Source can be absolute or relative to the csdef folder.
                Path source = element.GetAttribute("physicalDirectory");
                if (!source.IsRootPath())
                    source = Settings.ServiceDefinitionFile.GetFolder().Combine(source);
                if (Directory.Exists(source))
                    ConsoleProcess.CopyFolder(source, target);
                else
                    Trace.TraceError("Unable to find source directory at '{0}'.", source);
            }

            //|
            //| Process all child VirtualDirectory / VirtualApplication elements.
            //|
            foreach (var child in element.Elements())
            {
                CreateSiteDirectories(child, target);
            }
        }

        /// <summary>
        /// Creates the sites directories.
        /// </summary>
        /// <param name="targetRoot">The target root.</param>
        public void CopySites(Path targetRoot)
        {
            foreach (var site in Enumerable.ToList<XElement>(Settings.XServiceDefinition.XPathSelectElements("WebRole/Sites/Site")))
                CreateSiteDirectories(site, targetRoot.Combine(site.GetAttribute("name")));
        }


        private void BuildSolution(String virtualPath, BlobType blobType, Boolean overwrite, Boolean skipUpload)
        {
            /*
                Goal: create vhd file that stores sites source files, package file and source configuration.  
                      package file will have empty site directories.
                
             * 
             1. CreateVHD.bat
             2. Read in csdef 
             3. Loop through sites collectin
                - Read physical directory of each site
                - build /sites string for cspack
                - call copysites to VHD
              4.Call CSPack with sites override
              5.Copy package and serviceconfiguration file to VHD
              6.SetPermissions on VHD
              7.Unmount VHD
              8.Flag determines upload or stop
              9. 
            */


            //create VHD
            virtualPath = virtualPath.Trim('/', '\\', ' ');
            var resourceFiles = Path.GetFullPath(@".\Scripts");
            var createVHD = Path.Combine(resourceFiles, "createVhd.bat");
            var detachVhd = Path.Combine(resourceFiles, "detachVhd.bat");
            var createPackage = Path.Combine(resourceFiles, "createPackage.bat");
            var localVHD = Path.GetFullPath(Path.GetFileName(virtualPath));
            string cloudDriveSize = "512";
            string cloudDriveLetter = FindNextAvailableDriveLetter();
            Environment.SetEnvironmentVariable("_cloudDriveBlobUri", virtualPath);
            Environment.SetEnvironmentVariable("_cloudDriveFile", localVHD);
            if (overwrite)
                Environment.SetEnvironmentVariable("_cloudDriveOverwrite", overwrite.ToString());
            if (skipUpload)
                Environment.SetEnvironmentVariable("_cloudDriveSkipUpload", skipUpload.ToString());

            var args = string.Format("{0} {1} {2} {3}", cloudDriveLetter, cloudDriveSize, localVHD, (overwrite ? " /q" : String.Empty));
            ConsoleProcess.Start(createVHD, Path.GetFullPath("."), true, args);


            //Read in ServiceDefinition
            StringBuilder sitePhysicalDirectories = new StringBuilder();
            sitePhysicalDirectories.Append("/sitePhysicalDirectories:WebRole");
            Path cloudDriveLetterPath = string.Format("{0}:\\", cloudDriveLetter);
            String csdefDirectoryBuild = Path.Combine(Path.GetFullPath(Settings.ServiceDefinitionFile).GetFolder(), "_build");
            foreach (var site in Settings.XSites)
            {
                if (site.GetAttribute("name") == "Web") continue; //skip the first site

                string siteName = site.GetAttribute("name");
                string physicalDirectory = site.GetAttribute("physicalDirectory");

                Path emptySitePath = Path.Combine(csdefDirectoryBuild, siteName);
                //if (Directory.Exists(emptySitePath))
                //    Directory.Delete(emptySitePath);
                Directory.CreateDirectory(emptySitePath);

                //make web.config and copy it to empty site
                CreateWebConfigInEmptySite(emptySitePath);

                //copies the source files to the local vhd drive
                CopyDirectory(physicalDirectory, cloudDriveLetterPath + @"\" + siteName, true);

                //modify the physicalDirectory to point to an empty site
                site.SetAttributeValue("physicalDirectory", Path.Combine("_build", siteName));
            }
            string csdefBuildFile = Settings.ServiceConfigurationFile.GetFolder().Combine("ServiceDefinition.build.csdef");
            Settings.XServiceDefinition.Save(csdefBuildFile);

            //call cspack to package file
            var csPackArgs = string.Format(@"{0} /role:WebRole;site\WebRole;WindowsAzure.Accelerator.WebRole.dll; /rolePropertiesFile:WebRole;roleproperty.txt /noencrypt", csdefBuildFile);
            string csPackPath = Path.Combine(Environment.GetEnvironmentVariable("ProgramFiles"), @"Windows Azure SDK\v1.3\bin");
            ConsoleProcess.Start(Path.Combine(csPackPath, "cspack.exe"), Path.GetFullPath("."), true, csPackArgs);

            //Copy package and serviceconfiguration file to VHD
            string packageFileName = "ServiceDefinition.build.cspkg";
            string cscfgFile = Settings.ServiceConfigurationFile.GetFullPath();
            string packageFilePath = Path.Combine(Settings.ServiceDefinitionFile.GetFolder(), packageFileName);

            if (File.Exists(cscfgFile))
                File.Copy(cscfgFile, cloudDriveLetterPath + @"\" + Settings.ServiceConfigurationFile.GetFileName(), true);
            else
                throw new ApplicationException("ServiceConfiguration.cscfg file not found");

            if (File.Exists(packageFilePath))
                File.Copy(packageFilePath, cloudDriveLetterPath + @"\" + packageFileName, true);
            else
                throw new ApplicationException(packageFileName + " file not found");

            //SetPermissions on VHD
            SetDirectorySecurity(cloudDriveLetterPath, "Everyone");

            //Unmount VHD
            ConsoleProcess.Start(detachVhd, Path.GetFullPath("."), true, localVHD);

            //Upload Drive
            UploadFile(localVHD, virtualPath, BlobType.PageBlob, Switches.ContainsKey('q'));
        }

        private static void CreateWebConfigInEmptySite(string appPath)
        {
            string filePath = Path.Combine(appPath, "Web.Config");
            StreamWriter w;
            w = File.CreateText(filePath);
            w.WriteLine(WebConfig);
            w.Flush();
            w.Close();
        }

        private static string WebConfig
        {
            get
            {
                return @"
                    <?xml version='1.0' encoding='UTF-8'?>
                    <configuration>
                        <system.webServer>
                            <defaultDocument enabled='true'>
                                 <files>
                                    <clear />
                                    <add value='default.aspx' />
                                </files>
                            </defaultDocument>
                            <modules runAllManagedModulesForAllRequests='true' />
                        </system.webServer>
                    </configuration>
                    ";
            }
        }

        private static bool CopyDirectory(string SourcePath, string DestinationPath, bool overwriteexisting)
        {
            bool ret = false;
            try
            {
                SourcePath = SourcePath.EndsWith(@"\") ? SourcePath : SourcePath + @"\";
                DestinationPath = DestinationPath.EndsWith(@"\") ? DestinationPath : DestinationPath + @"\";

                if (Directory.Exists(SourcePath))
                {
                    if (Directory.Exists(DestinationPath) == false)
                        Directory.CreateDirectory(DestinationPath);

                    foreach (string fls in Directory.GetFiles(SourcePath))
                    {
                        FileInfo flinfo = new FileInfo(fls);
                        flinfo.CopyTo(DestinationPath + flinfo.Name, overwriteexisting);
                    }
                    foreach (string drs in Directory.GetDirectories(SourcePath))
                    {
                        DirectoryInfo drinfo = new DirectoryInfo(drs);
                        if (CopyDirectory(drs, DestinationPath + drinfo.Name, overwriteexisting) == false)
                            ret = false;
                    }
                }
                ret = true;
            }
            catch (Exception ex)
            {
                ret = false;
            }
            return ret;
        }

        private void SetDirectorySecurity(string dir, string user)
        {
            try
            {

                DirectorySecurity dir_security = Directory.GetAccessControl(dir);
                FileSystemAccessRule access_rule = new FileSystemAccessRule(user, FileSystemRights.FullControl, AccessControlType.Allow);
                dir_security.AddAccessRule(access_rule);
                Directory.SetAccessControl(dir, dir_security);
            }
            catch (Exception ex)
            {
                throw new ApplicationException("Setting directory security failed");
            }
        }



        private string FindNextAvailableDriveLetter()
        {
            // build a string collection representing the alphabet
            StringCollection alphabet = new StringCollection();

            int lowerBound = Convert.ToInt16('a');
            int upperBound = Convert.ToInt16('z');
            for (int i = upperBound; i > lowerBound; i--)
            {
                char driveLetter = (char)i;
                alphabet.Add(driveLetter.ToString());
            }

            // get all current drives
            DriveInfo[] drives = DriveInfo.GetDrives();
            foreach (DriveInfo drive in drives)
            {
                alphabet.Remove(drive.Name.Substring(0, 1).ToLower());
            }

            if (alphabet.Count > 0)
            {
                return alphabet[0];
            }
            else
            {
                throw (new ApplicationException("No drives available."));
            }
        }


        /// <summary>
        /// Uploads the file.
        /// </summary>
        /// <param name="localPath">The local path.</param>
        /// <param name="virtualPath">The virtual path.</param>
        /// <param name="blobType">Type of the BLOB.</param>
        /// <param name="overwrite">if set to <c>true</c> [overwrite BLOB if exists].</param>
        /// <param name="skipUpload">if set to <c>true</c> [skip upload].</param>
        private void PublishWeb(String localPath, String virtualPath, BlobType blobType, Boolean overwrite, Boolean skipUpload)
        {
            //if (Settings.IsHostableWebCore)
            //{
            virtualPath = virtualPath.Trim('/', '\\', ' ');
            localPath = Path.GetFullPath(localPath);
            var resourceFiles = Path.GetFullPath(@".\Resources");
            var publishCMD = Path.GetFullPath(@".\PublishApplication.cmd");
            var createVHD = Path.Combine(resourceFiles, "createVhd.cmd");
            var unmountVHD = Path.Combine(resourceFiles, "unmountVhd.cmd");
            var localVHD = Path.GetFullPath(Path.GetFileName(virtualPath));
            Environment.SetEnvironmentVariable("_cloudDriveBlobUri", virtualPath);
            Environment.SetEnvironmentVariable("_cloudDriveFile", localVHD);
            Environment.SetEnvironmentVariable("_cloudDriveSource", localPath);
            if (overwrite)
                Environment.SetEnvironmentVariable("_cloudDriveOverwrite", overwrite.ToString());
            if (skipUpload)
                Environment.SetEnvironmentVariable("_cloudDriveSkipUpload", skipUpload.ToString());
            var args = (Settings.ApplicationName ?? "IIS") + (overwrite ? " /q" : String.Empty);
            ConsoleProcess.Start(publishCMD, Path.GetFullPath("."), true, args);
            //}
            //else
            //{
            //    //i|
            //    //i| Check for sites definitions
            //    //i|
            //    //String drive = "Z:";
            //    //CopySites(drive);

            //}
        }

        /// <summary>
        /// Uploads the file.
        /// </summary>
        /// <param name="localPath">The local path.</param>
        /// <param name="virtualPath">The virtual path.</param>
        /// <param name="blobType">Type of the BLOB.</param>
        /// <param name="overwrite">if set to <c>true</c> [overwrite BLOB if exists].</param>
        private void UploadFile(String localPath, String virtualPath, BlobType blobType, Boolean overwrite)
        {
            virtualPath = virtualPath.Trim('/', '\\', ' ');
            String path = Path.GetFullPath(localPath);
            if (!File.Exists(path))
                throw new FileNotFoundException("The system cannot find the file specified.", path);
            CloudBlobClient client = Settings.CloudStorageAccount.CreateCloudBlobClient();
            CloudBlob blob = client.GetBlobReference(virtualPath);
            if ( blob.Exists() && !overwrite )
            {
                Console.Error.Write("\r\nA blob already exists at storage location '{0}'.\r\n\r\nDo you wish to overwrite (y/n)? ", blob.Uri);
                ConsoleKeyInfo result = Console.ReadKey(false);
                if ( Char.ToLower(result.KeyChar) != 'y' )
                    return;                
            }
            Console.WriteLine("\r\nUploading file to storage...\r\nSource:  '{0}'\r\nTarget:  '{1}'", path, blob.Uri);
            try
            {
                blob.Container.CreateIfNotExist();
                if (blobType == BlobType.PageBlob)
                    //blob.ToPageBlob.UploadCloudDrive(new FileInfo(path));
                    Settings.CloudStorageAccount.UploadVhd(localPath, virtualPath, true);
                else
                    Settings.CloudStorageAccount.UploadFile(localPath, virtualPath, true, true);
            }
            catch (Exception ex)
            {
                Console.WriteLine("An error occurred during upload.\r\n{0}.", ex.FormatException());
                return;
            }
            Console.WriteLine("Upload complet.");
        }

        /// <summary>
        /// Uploads the file.
        /// </summary>
        /// <param name="virtualPath">The virtual path.</param>
        /// <param name="localPath">The local path.</param>
        /// <param name="overwrite">if set to <c>true</c> overwrites any existing file.</param>
        private void DownloadFile(String virtualPath, String localPath, Boolean overwrite)
        {
            virtualPath = virtualPath.Trim('/', '\\', ' ');
            String path = Path.GetFullPath(localPath);
            if (File.Exists(path) && !overwrite)
            {
                Console.Error.Write("\r\nA file already exists at '{0}'.\r\n\r\nDo you wish to overwrite (y/n)? ", path);
                ConsoleKeyInfo result = Console.ReadKey(false);
                if ( Char.ToLower(result.KeyChar) != 'y' )
                    return;
            }
            CloudBlob blob = Settings.CloudStorageAccount.CreateCloudBlobClient().GetBlobReference(virtualPath);
            if (blob == null || !blob.Exists())
                throw new FileNotFoundException("The system could not find a blob at the storage location specified.", blob.OnValid(b => b.Uri.ToString()) ?? virtualPath);
            Console.WriteLine("\r\nDownloading blob from storage...\r\nSource:  '{0}'\r\nTarget:  '{1}'", blob.Uri, path);
            Settings.CloudStorageAccount.DownloadToFile(localPath, virtualPath, true);
            Console.WriteLine("Download complete.");
        }

        /// <summary>
        /// Synches the BLOB storage.
        /// </summary>
        /// <param name="syncDirection">The sync direction.</param>
        /// <param name="containerUri">The container URI.</param>
        /// <param name="localPath">The local path.</param>
        /// <returns></returns>
        private void SynchBlobStorage(SyncDirectionOrder syncDirection, String containerUri, String localPath)
        {
            containerUri = containerUri.Trim('/', '\\', ' ');
            localPath = Path.GetFullPath(localPath);
            var syncManager = new SyncManager(SyncProviderType.CloudSync)
                                          {
                                              ContainerName = containerUri,
                                              LocalPath = localPath,
                                              Interval = TimeSpan.FromSeconds(0),
                                              Account = Settings.CloudStorageAccount
                                          };
            syncManager.Sync();
        }

        /// <summary>
        /// Creates the service bus service console.
        /// </summary>
        /// <param name="servicePathOverride">The service path override.</param>
        private void CreateServiceConsole(String servicePathOverride)
        {
            if (!String.IsNullOrEmpty(servicePathOverride))
                Settings.ServiceBusConnection.ServicePath = servicePathOverride;
            Console.WriteLine("[( Service Console Client )]");
            Console.WriteLine("Connecting to Azure Service Bus...");
            Console.WriteLine("\r\n{0}\r\n", Settings.ServiceBusConnection.ToTraceString());

            ServiceConsole.CreateClientConsole(Settings.ServiceBusConnection);            
        }

        /// <summary>
        /// Creates the real-time trace listener.
        /// </summary>
        /// <param name="servicePathOverride">The service path override.</param>
        private void CreateTraceListener(String servicePathOverride)
        {
            if ( !String.IsNullOrEmpty(servicePathOverride) )
                Settings.ServiceBusConnection.ServicePath = servicePathOverride;

            Console.WriteLine("[( Trace Console Server )]");
            Console.WriteLine("Connecting to Azure Service Bus...");

            //i| Create the Service Host 
            var host = new ServiceHost(typeof(CloudTraceService), Settings.ServiceBusConnection.GetServiceUri());
            var serviceEndPoint = host.AddServiceEndpoint(typeof(ICloudTraceContract), new NetEventRelayBinding(), String.Empty);
            serviceEndPoint.Behaviors.Add(Settings.ServiceBusConnection.GetTransportClientEndpointBehavior());

            //i| Open the Host
            host.Open();
            Console.WriteLine("Connected to: {0}", Settings.ServiceBusConnection.GetServiceUri());
            Console.WriteLine("Hit [Enter] to exit");

            //i| Wait Until the Enter Key is Pressed and Close the Host
            Console.ReadLine();
            host.Close();
        }
    }
}